/*!
 * \file WtFilterMgr.cpp
 * \project	WonderTrader
 *
 * \author Wesley
 * \date 2020/03/30
 * 
 * \brief 过滤管理器实现文件
 * 
 * 本文件实现了过滤管理器的核心功能，主要包括：
 * 1. 过滤配置的动态加载和重载
 * 2. 策略信号的过滤处理逻辑
 * 3. 合约代码的过滤检查机制
 * 4. 执行器通道的启用/禁用控制
 * 5. 过滤动作的执行（忽略/重定向）
 * 
 * 该实现通过配置文件驱动的方式提供灵活的过滤控制，
 * 支持运行时动态调整，为交易系统提供重要的风险控制能力。
 */
#include "WtFilterMgr.h"
#include "EventNotifier.h"

#include "../Share/CodeHelper.hpp"
#include "../Includes/WTSVariant.hpp"
#include "../WTSUtils/WTSCfgLoader.h"
#include "../WTSTools/WTSLogger.h"

#include <boost/filesystem.hpp>

USING_NS_WTP;

/**
 * @brief 加载过滤器配置
 * @param fileName 配置文件名
 * 
 * 从配置文件加载过滤规则，支持以下配置节：
 * 1. strategy_filters：策略过滤器，支持ignore和redirect动作
 * 2. code_filters：合约/品种过滤器，支持ignore和redirect动作
 * 3. executer_filters：执行器过滤器，支持启用/禁用控制
 * 
 * 支持配置文件的动态重载，当文件修改时间戳变化时自动重新加载
 */
void WtFilterMgr::load_filters(const char* fileName)
{
	if (_filter_file.empty() && (strlen(fileName) == 0))
		return;

	if(strlen(fileName) > 0)
		_filter_file = fileName;

	if (!StdFile::exists(_filter_file.c_str()))
	{
		WTSLogger::debug_f("Filters configuration file {} not exists", _filter_file);
		return;
	}

	uint64_t lastModTime = boost::filesystem::last_write_time(boost::filesystem::path(_filter_file));
	if (lastModTime <= _filter_timestamp)
		return;

	if (_filter_timestamp != 0)
	{
		WTSLogger::info_f("Filters configuration file {} modified, will be reloaded", _filter_file);
		if (_notifier)
			_notifier->notifyEvent("Filter file has been reloaded");
	}

	WTSVariant* cfg = WTSCfgLoader::load_from_file(_filter_file.c_str(), true);

	_filter_timestamp = lastModTime;

	_stra_filters.clear();
	_code_filters.clear();
	_exec_filters.clear();

	//加载策略过滤器
	WTSVariant* filterStra = cfg->get("strategy_filters");
	if (filterStra)
	{
		auto keys = filterStra->memberNames();
		for (const std::string& key : keys)
		{
			WTSVariant* cfgItem = filterStra->get(key.c_str());
			const char* action = cfgItem->getCString("action");
			FilterAction fAct = FA_None;
			if (wt_stricmp(action, "ignore") == 0)
				fAct = FA_Ignore;
			else if (wt_stricmp(action, "redirect") == 0)
				fAct = FA_Redirect;

			if (fAct == FA_None)
			{
				WTSLogger::error_f("Action {} of strategy filter {} not recognized", action, key);
				continue;
			}

			FilterItem& fItem = _stra_filters[key];
			fItem._key = key;
			fItem._action = fAct;
			fItem._target = cfgItem->getDouble("target");

			WTSLogger::info_f("Strategy filter {} loaded", key);
		}
	}

	//加载代码过滤器
	WTSVariant* filterCodes = cfg->get("code_filters");
	if (filterCodes)
	{
		auto codes = filterCodes->memberNames();
		for (const std::string& stdCode : codes)
		{

			WTSVariant* cfgItem = filterCodes->get(stdCode.c_str());
			const char* action = cfgItem->getCString("action");
			FilterAction fAct = FA_None;
			if (wt_stricmp(action, "ignore") == 0)
				fAct = FA_Ignore;
			else if (wt_stricmp(action, "redirect") == 0)
				fAct = FA_Redirect;

			if (fAct == FA_None)
			{
				WTSLogger::error_f("Action {} of code filter {} not recognized", action, stdCode);
				continue;
			}

			FilterItem& fItem = _code_filters[stdCode];
			fItem._key = stdCode;
			fItem._action = fAct;
			fItem._target = cfgItem->getDouble("target");

			WTSLogger::info_f("Code filter {} loaded", stdCode);
		}
	}

	//执行通道过滤器
	WTSVariant* filterExecuters = cfg->get("executer_filters");
	if (filterExecuters)
	{
		auto executer_ids = filterExecuters->memberNames();
		for (const std::string& execid : executer_ids)
		{
			bool bDisabled = filterExecuters->getBoolean(execid.c_str());
			WTSLogger::info_f("Executer {} is %s", execid, bDisabled?"disabled":"enabled");
			_exec_filters[execid] = bDisabled;
		}
	}

	cfg->release();
}

/**
 * @brief 检查执行器是否被过滤
 * @param execid 执行器ID
 * @return 是否被过滤（禁用）
 * 
 * 检查指定执行器是否在过滤列表中被禁用
 */
bool WtFilterMgr::is_filtered_by_executer(const char* execid)
{
	auto it = _exec_filters.find(execid);
	if (it == _exec_filters.end())
		return false;

	return it->second;
}

/**
 * @brief 过滤动作名称数组
 * 
 * 用于日志输出时显示过滤动作的可读名称
 */
const char* FLTACT_NAMEs[] =
{
	"Ignore",
	"Redirect"
};

/**
 * @brief 检查策略是否被过滤
 * @param straName 策略名称
 * @param targetPos 目标持仓，可能被过滤器修改
 * @param isDiff 是否为差量信号
 * @return 是否被过滤
 * 
 * 对策略信号进行过滤处理：
 * 1. 对于差量信号，如果被过滤则直接忽略
 * 2. 对于绝对持仓信号，根据过滤动作进行处理：
 *    - ignore：返回true，信号被完全过滤
 *    - redirect：返回false，但调整目标持仓为配置值
 */
bool WtFilterMgr::is_filtered_by_strategy(const char* straName, double& targetPos, bool isDiff /* = false */)
{
	auto it = _stra_filters.find(straName);
	if (it != _stra_filters.end())
	{
		const FilterItem& fItem = it->second;
		if(isDiff)
		{
			//如果是差量信号被策略过滤器命中，就直接过滤掉
			WTSLogger::info("[Filters] Strategy filter %s triggered, the change of position ignored directly", straName);
			return true;
		}

		WTSLogger::info("[Filters] Strategy filter %s triggered, action: %s", straName, fItem._action <= FA_Redirect ? FLTACT_NAMEs[fItem._action] : "Unknown");
		if (fItem._action == FA_Ignore)
		{
			return true;
		}
		else if (fItem._action == FA_Redirect)
		{
			//只有不是差量信号时，才有效
			targetPos = fItem._target;
		}

		return false;
	}

	return false;
}

/**
 * @brief 检查合约代码是否被过滤
 * @param stdCode 标准合约代码
 * @param targetPos 目标持仓，可能被过滤器修改
 * @return 是否被过滤
 * 
 * 对合约代码进行过滤处理：
 * 1. 首先检查完整合约代码是否有过滤规则
 * 2. 如果没有，再检查对应的品种代码是否有过滤规则
 * 3. 根据过滤动作进行相应处理（忽略或重定向）
 */
bool WtFilterMgr::is_filtered_by_code(const char* stdCode, double& targetPos)
{
	auto cit = _code_filters.find(stdCode);
	if (cit != _code_filters.end())
	{
		const FilterItem& fItem = cit->second;
		WTSLogger::info("[Filters] Code filter %s triggered, action: %s", stdCode, fItem._action <= FA_Redirect ? FLTACT_NAMEs[fItem._action] : "Unknown");
		if (fItem._action == FA_Ignore)
		{
			return true;
		}
		else if (fItem._action == FA_Redirect)
		{
			targetPos = fItem._target;
		}

		return false;
	}

	std::string stdPID = CodeHelper::stdCodeToStdCommID(stdCode);
	cit = _code_filters.find(stdPID);
	if (cit != _code_filters.end())
	{
		const FilterItem& fItem = cit->second;
		WTSLogger::info("[Filters] CommID filter %s triggered, action: %s", stdPID.c_str(), fItem._action <= FA_Redirect ? FLTACT_NAMEs[fItem._action] : "Unknown");
		if (fItem._action == FA_Ignore)
		{
			return true;
		}
		else if (fItem._action == FA_Redirect)
		{
			targetPos = fItem._target;
		}

		return false;
	}

	return false;
}



